unit KRK.Components.AdditionalControls.DefinedCrypt;
{ 22/11/2004
	DefinedCrypt vers?o 4.0
	By Carlos Barreto Feitoza Filho (ICQ#29391688 - Authorize)
	carlos.b.feitoza.filho@gmail.com
	??????????????????????????????????????????????????????????????????????????????
	Hist?ria:
  Um dia precisei de  um algoritmo simples de  criptografia para usar em   um de
  meus programas,  com o intuito  de  encriptar strings de texto. Como n?o  tive
  paci?ncia para criar um, resolvi procurar  na Internet. Achei o que eu  estava
  procurando,  mas ocorreu  uma coisa  muito  inesperada...   O  algoritmo   era
  gen?rico,   ou  seja,   ele manipulava   a  seq??ncia  de  texto   entrada   e
  fazia um   c?lculo simples,  alterando os  caracteres um  a um.  Isso seria  o
  ideal, mas infelizmente  ele  utilizava  todos os  256 caracteres do  conjunto
  ASCII, quer  dizer, ele tamb?m  inclu?a  em seus c?lculos todos  os caracteres
  de controle  (DEL, CR, LF,   ACK, etc.).  Descobri isso quando  encriptava uma
  string que seria salva em um INI. Quando eu a desencriptava  ela sempre  vinha
  pela metade  e  dava  um puta  erro! Foi  ent?o que  eu percebi  o que  estava
  acontecendo:  A  String  que   eu  estava   processando,   gerava    ap?s  ser
  encriptada  um  caractere especial  (CR). Como nos INIs se salvam uma linha de
  cada vez, eu s? estava  salvando a string at? o CR e  o que vinha ap?s ele era
  sumariamente  ignorado.  Foi ent?o que  eu lembrei  do JavaScript! Em   JS tem
	muitos exemplos pra fazer  criptografia   de strings, e al?m  disso,  como  JS
  n?o  ?  uma  linguagem  compilada   e  sim  uma Linguagem  Interpretada  seria
  muito mais f?cil  encontrar o que  eu procurava. Esse   m?dulo, portanto,  foi
  INTEIRAMENTE  convertido   e  otimizado    a  partir    do  Script   VIRGENERE
	ENCRYPTION  criado por Francisco  Ryan  Tolmasky  (opossum@mailcity.com). Esse
	Script usa uma tabela fixa  (Array  de Strings) contendo  pares  de caracteres
	comuns em meu  idioma, nunca saindo  desse intervalo, ou  seja, as fun??es  s?
	usam  esses   caracteres   para   criptografar  ficando  de   fora  caracteres
	indesejados! O  objetivo do DefinedCrypt ? portanto codificar um texto  usando
	sua pr?pria tabela  interna de pares  de caracteres. Isso evita o aparecimento
	de caracteres indesejados no texto codificado, por?m  PODE  TORNAR O  PROCESSO
	DE (DES)CRIPTOGRAFIA MUITO LENTO mesmo em computadores r?pidos. Essa ? a ?nica
	desvantagem que eu vejo no DefinedCrypt.

	M?TODOS DE TDEFINEDCRYPT
    ????????????????????????
	- function SingleEncryptDecrypt(Senha: TKey;
                                  Entrada: String;
                                  Funcao: TDCFuncao
									                ): String;

	A  fun??o  SingleEncryptDecrypt   retorna  a   String  contida   em  'Entrada'
	(des)criptografada de acordo com a string contida em 'Senha'

	Senha = Uma  sequencia  de  texto que tem  de  ser  a mesma quando for feita a
			   (des)encripta??o. Isso parece l?gico, por isso o nome de SENHA!
	Entrada = String  de  Texto a ser (des)criptografada. O  tamanho te?rico dessa
			      string ? 2GB.
	Fun??o = Constante que identifica o processo a ser usado pela fun??o. Pode ser
			     ou "dcEncriptar" ou "dcDecriptar" (sem aspas!)


	- function PublicKeyEncrypt(PublicKey: TKey;
                              DecriptedText: String): String;

	Esta  fun??o  encripta  o texto  contido em  'DecriptedText' segundo uma chave
	p?blica passada como par?metro em  'PublicKey'.  Este tipo  de criptografia  ?
	mais seguro do que  SingleEncryptDecrypt pois  usa um sistema assim?trico,  ou
	seja, aquele onde existe uma chave que encripta os  dados (PublicKey) e  outra
	diferente para desencripta-los (PrivateKey)

	PublicKey = Uma  sequencia  de  caracteres  com  a  qual  o  texto  contido em
				      'DecriptedText' ser? criptografado. Use a  fun??o  GenerateKeyPair
              para criar pares de chaves p?blicas e privadas.
	DecriptedText = O  texto que  dever? ser criptogrado.  O tamanho te?rico desse
					        texto ? 2GB.


	- function PrivateKeyDecrypt(PrivateKey: TKey;
								               EncryptedText: String): String;

	Esta  fun??o descriptografa o texto  contido em  'EncryptedText'  segundo  uma
	chave  privada  passada como par?metro em 'PrivateKey'. A chave privada tem de
	ser o par correspondente ? chave  p?blica que foi  usada na  criptografia  com
	PublicKeyEncrypt.  Use a fun??o  GenerateKeyPair  para criar  pares de  chaves
	p?blicas e privadas.

	PrivateKey = Uma sequencia  de  caracteres  com  a  qual  o  texto  contido em
				       'EncryptedText'    ser?    descriptografado.    Use    a   fun??o
				       GenerateKeyPair para criar pares de chaves p?blicas e privadas.
	EncryptedText = O texto que dever? ser descriptogrado. O tamanho te?rico desse
					        texto ? 2GB.

	-	function GenerateKeyPair: TKeyPair;

	Esta fun??o gera um par de chaves (uma p?blica e outra privada) ?nicas que
	podem ser usadas com as fun??es PrivateKeyDecrypt e PublicKeyEncrypt.


	EVENTOS
	???????
	- OnUpdateProgress

	type
		TUpdateProgress = procedure (Sender: TObject; Posicao: Integer) of object;

	FUpdateProgress: TUpdateProgress;
	
	property OnUpdateProgress: TUpdateProgress
      read FUpdateProgress
	   write FUpdateProgress

	Retorna a cada (des)encripta??o de um ?nico caractere o n?mero deste caractere
	dentro da string. O  primeiro caractere na string  ?  1 o segundo ? 2 e  assim
  por  diante at?  que Posicao se torne igual ao total de  caracteres na  string
	sendo (des)encriptada,  indicando o  fim do  processo  de  (des)encripta??o. O
	objetivo  desse  evento ? retornar um valor que aumenta a media que o processo
  est? sendo executado, para ser usado em um retorno visual  como em  barras  de
	progresso.


	PROPRIEDADES
	????????????
	- FullCharacterSet

	FFullCharacterSet: Boolean;

	property FullCharacterSet: Boolean 
	    read FFullCharacterSet
	   write FFullCharacterSet;

	Essa propriedade ? True por padr?o e neste caso, TODOS OS CARACTERES da tabela
	ASCII ser?o usados na criptografia de textos. Esse m?todo ? prefer?vel para  a
	encripta??o  de  textos  que  ser?o  salvos  em arquivos.  E ? terminantemente
	desaconselhada  quando  o resultado  de uma criptografia dever? ser exibido em
	componentes  visuais  como  Memos.  Isto  porque  muitas  vezes  o processo de
	criptografia  nesse modo  gera  caracteres  de  controle que os componentes de
	exibi??o de texto interpretam erroneamente o que altera o texto criptografado.
	Quando FullCharacterSet = False, nenhum caractere de controle ser?  utilizado,
	logo  o texto resultante  de um criptografia  pode ser  exibido em componentes
	visuais ou mesmo enviado via e-mail para uma posterior descriptografia.


	HIST?RICO DE VERS?ES
	????????????????????
	vers?o 1.0: Nada foi acrescentado. Essa vers?o ? apenas uma convers?o total do
              JavaScript para pascal. Apenas uma unit simples;
	vers?o 2.0: Ap?s  algum  tempo  de  uso  descobri  que as senhas n?o eram case
              sensitive,  isto  ?,  tanto  fazia  se  voc?  digitasse  "Carlos",
              "carlos" ou mesmo "CarLoS" como senha!  Isso  est?  longe  de  ser
              seguro!  Alterei  a  inclus?o  de  senha  para  que  a  mesma seja
              encriptada sozinha antes de ser efetuada a criptografia  do texto,
              dessa  forma  cada  senha  ir?  gerar  um   texto  (des)encriptado
              completamente diferente mesmo se apenas 1 caractere da senha tiver
              sido alterado, seja quanto ao caso (mai?sculo/minusculo), ou  seja
              quanto a quantidade de caracteres na senha!
	vers?o 3.0: Transforma??o  da  unit simples em   um   componente   n?o  visual
              instal?vel  na  paleta  de  componentes do Delphi  que oferece  um
				      evento (OnUpdateProgress) para ser usado na atualiza??o de  barras
				      de progresso!
	vers?o 4.0: Descobri  que o  encriptador de  senha  que  usava a  fim de obter
				      textos (des)encriptados 100% diferentes quando se mudava apenas  1
				      caractere (v2.0) n?o estava funcionando corretamente. Reescrevi  a
				      fun??o chamando-a de MakeHashFromKey que leva  em  considera??o  a
				      quantidade de caracteres, o caso e a arruma??o das letras na senha
				      e ?  capaz de gerar  um texto  95% diferente   quando  se altera o
				      caso,  a  posi??o  e  a  quantidade  de caracteres na senha. Nesta
				      vers?o  foram  inclu?das   mais  duas  fun??es  para  encriptar  e
				      desencriptar com chaves privadas e p?blicas  respectivamente.  Uma
				      nova propriedade foi inclu?da (FullCharacterSet) que limita o  uso
				      de caracteres pelo componente.
  vers?o 5.0: Ajustes para Delphi XE5. Foram removidas  todas as refer?ncias  ao
              objeto Application e consequente remo??o da depend?ncia com a unit
              Forms. A fun??o para gerar hashes foi simplificada e agora usa  as
              fun??es  do Krakatoa. A  propriedade FullCharset  foi removida. Em
              seu lugar foi criada uma nova propriedade  CryptTable para indicar
              a tabela de criptografia a ser usada
}

interface

uses Classes, Windows, KRK.Lib.CryptoAPI.Types;

type
	TCryptTableFullInterval         = 0..127; // 128 pares (256 caracteres)
	TCryptTableNoControlInterval    = 0..111; // 112 pares (224 caracteres)
  TCryptTableSafeInterval         = 0..47;  // 48 pares (96 caracteres)
	TPairIndex                      = 0..1;
	TPair                           = array [TPairIndex] of Char;
	TKey                            = String;
	TMaxIndex                       = 0..(2 * High(TCryptTableFullInterval) + 1);

	TKeyPair = packed record
		PrivateKey: TKey;
		PublicKey : TKey;
	end;

	TDCFunction = (dcEncrypt, dcDecrypt);

  TCryptTable = (ctFull, ctNoControl, ctSafe);

	TUpdateProgress = procedure (Sender: TObject; Posicao: Integer) of object;

	TDefinedCrypt = class(TComponent)
	private
//		FFullCharacterSet: Boolean;
		FUpdateProgress: TUpdateProgress;
    FCryptTable: TCryptTable;
		function GetIndexFromPair(aLetter: Char; out aPairIndex: TPairIndex): TCryptTableFullInterval;
    function CheckIfExist(aLetter: Char): Boolean;
		function FixOffset(aIndex: TMaxIndex): TCryptTableFullInterval;
    function MakeHashFromKey(const aKey: TKey): TKey;
	public
		constructor Create(aOwner: TComponent); override;
		function SingleEncryptDecrypt(aPassword: TKey; aInput: String; aFunction: TDCFunction): String;
		function PublicKeyEncrypt(aPublicKey: TKey; aDecriptedText: String): String;
		function PrivateKeyDecrypt(aPrivateKey: TKey; aEncryptedText: String): String;
		function GenerateKeyPair: TKeyPair;
	published
//		property FullCharacterSet: Boolean read FFullCharacterSet write FFullCharacterSet;
    property CryptTable: TCryptTable read FCryptTable write FCryptTable default ctFull;
		property OnUpdateProgress: TUpdateProgress read FUpdateProgress write FUpdateProgress;
	end;

{$i CryptTables.inc}

implementation

uses KRK.Lib.CryptoAPI.Utilities, System.SysUtils;

constructor TDefinedCrypt.Create(aOwner: TComponent);
begin
  inherited;
//  FFullCharacterSet := True;
  FCryptTable := ctFull;
end;

(* A fun??o   GetIndexFromPair  a  seguir   faz  duas coisas,   retorna o indice
do elemento em  "CryptTableFull"  ou  "CryptTableNoControle"  no  qual  a  Letra
passada como par?metro se encontra e, caso encontre,  altera o valor da variavel
passada como par?metro em PairIndex com um  valor que identifica qual   dos dois
caracteres do par a letra ? *)
function TDefinedCrypt.GetIndexFromPair(aLetter: Char; out aPairIndex: TPairIndex): TCryptTableFullInterval;
var
	i: TCryptTableFullInterval;
begin
	Result := 0;

  case FCryptTable of
    ctFull: begin
      (* Circulando por todos os elementos de CryptTable... *)
      for i := 0 to High(TCryptTableFullInterval) do
        (* Se  o primeiro  item (0) do par no elemento  atual  for igual a
        letra... *)
        if CryptTableFull[i][0] = aLetter then
        begin
          aPairIndex := 0; // ?ndice da letra dentro do par
          Result := i;     // ?ndice dentro de CryptTable onde a letra est?
          Break;           // Sai imediatamente
        end
        else
          (* Se o  segundo item  (1) do  par no elemento atual for igual a
          letra... *)
          if CryptTableFull[i][1] = aLetter then
          begin
            aPairIndex := 1;
            Result := i;
            Break;
          end;
    end;
    ctNoControl: begin
      for i := 0 to High(TCryptTableNoControlInterval) do
        if CryptTableNoControl[i][0] = aLetter then
        begin
          aPairIndex := 0;
          Result := i;
          Break;
        end
        else
          if CryptTableNoControl[i][1] = aLetter then
          begin
            aPairIndex := 1;
            Result := i;
            Break;
          end;
    end;
    ctSafe: begin
      for i := 0 to High(TCryptTableSafeInterval) do
        if CryptTableSafe[i][0] = aLetter then
        begin
          aPairIndex := 0;
          Result := i;
          Break;
        end
        else
          if CryptTableSafe[i][1] = aLetter then
          begin
            aPairIndex := 1;
            Result := i;
            Break;
          end;
    end;
  end;
end;

(* A  fun??o  abaixo   (CheckIfExist)  faz   uma  verifica??o  simples  buscando
pelo caractere indicado  e retornando True  ou False  de  acordo com a  presen?a
deste   caractere   dentro   de   "CryptTableFull"   ("CryptTableNoControl") *)
function TDefinedCrypt.CheckIfExist(aLetter: Char): Boolean;
var
	i: TCryptTableFullInterval;
begin
	Result := False;

  case FCryptTable of
    ctFull: begin
      for i := 0 to High(TCryptTableFullInterval) do
        (* Se a  letra sendo  checada pertencer  a um dos  dois  itens  do
        par sendo testado ent?o  a fun??o retorna true  imediatamente  e o
        loop ? quebrado *)
        if (CryptTableFull[i][0] = aLetter) or (CryptTableFull[i][1] = aLetter) then
        begin
          Result := True;
          Break;
        end;
    end;
    ctNoControl: begin
      for i := 0 to High(TCryptTableNoControlInterval) do
        if (CryptTableNoControl[i][0] = aLetter) or (CryptTableNoControl[i][1] = aLetter) then
        begin
          Result := True;
          Break;
        end;
    end;
    ctSafe: begin
      for i := 0 to High(TCryptTableSafeInterval) do
        if (CryptTableSafe[i][0] = aLetter) or (CryptTableSafe[i][1] = aLetter) then
        begin
          Result := True;
          Break;
        end;
    end;
  end;
end;

(* As fun??es abaixo mant?m os procedimentos sempre atuando no intervalo de  0 a
High(TCryptTableFullInterval), retornando um valor v?lido num deste intervalo *)
function TDefinedCrypt.FixOffset(aIndex: TMaxIndex): TCryptTableFullInterval;
begin
  case FCryptTable of
    ctFull: begin
      if aIndex > High(TCryptTableFullInterval) then
        Result := (aIndex - (High(TCryptTableFullInterval) + 1))
      else
        Result := aIndex;
    end;
    ctNoControl: begin
      if aIndex > High(TCryptTableNoControlInterval) then
        Result := (aIndex - (High(TCryptTableNoControlInterval) + 1))
      else
        Result := aIndex;
    end;
    ctSafe: begin
      if aIndex > High(TCryptTableSafeInterval) then
        Result := (aIndex - (High(TCryptTableSafeInterval) + 1))
      else
        Result := aIndex;
    end;
    else
      raise Exception.Create('Vetor criptogr?fico n?o definido');
  end;
end;

function TDefinedCrypt.MakeHashFromKey(const aKey: TKey): TKey;
begin
  Result := GetStringCheckSum(aKey,[haSha512]);

  if (Length(aKey) mod 2) = 0 then
    Result := LowerCase(Result)
end;

function TDefinedCrypt.SingleEncryptDecrypt(aPassword: TKey; aInput: String; aFunction: TDCFunction): String;
var
	TamanhoDaEntrada, IndiceNaEntrada: Cardinal;
	PairIndex: TPairIndex;
	StringTemporaria: String;
	IndiceFinalDaSenha, IndiceNaSenha, Shift, OriginalIndex: Byte;
	aSenha: TKey;
begin
	if (aPassword = '') or (aInput = '') then
	begin
		MessageBox(0,'Para que a (des)criptografia seja poss?vel ? necess?rio que voc? informe a senha e o texto a ser (des)criptografado. Corrija os par?metros da fun??o EncryptDecrypt','Par?metro inv?lido',MB_ICONWARNING);
		Exit;
	end;
	(* A vari?vel  aSenha abaixo recebe um valor gerado a partir da senha digitada
	por?m apenas com caracteres v?lidos. A gera??o desta senha se d? levando-se em
	considera??o  o  tamanho  e o  caso dos caracteres nala contida assim como seu
	tamanho *)
	aSenha := MakeHashFromKey(aPassword);
	
	IndiceFinalDaSenha := Length(aSenha); //Indice do ultimo caractere na Senha
	TamanhoDaEntrada := Length(aInput);
	IndiceNaEntrada := 1; //Armazena o indice na strinde de entrada
	IndiceNaSenha := 1; //Armazena o indice na sring de senha
	StringTemporaria := ''; //String com a string a ser (des)criptografada
	Shift := 0;

	(* ====== para uso em barras de progresso ====== *)
	if Assigned(OnUpdateProgress) then
		OnUpdateProgress(Self,IndiceNaEntrada);
	(* ====== para uso em barras de progresso ====== *)

	repeat
		if CheckIfExist(aInput[IndiceNaEntrada]) then
		begin
			case aFunction of
				dcEncrypt: Shift := GetIndexFromPair(aSenha[IndiceNaSenha],PairIndex);
				dcDecrypt: begin
          case FCryptTable of
            ctFull: Shift := (High(TCryptTableFullInterval) + 1);
            ctNoControl: Shift := (High(TCryptTableNoControlInterval) + 1);
            ctSafe: Shift := (High(TCryptTableSafeInterval) + 1);
          end;
          Shift := Shift - GetIndexFromPair(aSenha[IndiceNaSenha],PairIndex)
				end;
			end;

			OriginalIndex := GetIndexFromPair(aInput[IndiceNaEntrada],PairIndex);

      case FCryptTable of
        ctFull: StringTemporaria := StringTemporaria + CryptTableFull[FixOffset(OriginalIndex + Shift)][PairIndex];
        ctNoControl: StringTemporaria := StringTemporaria + CryptTableNoControl[FixOffset(OriginalIndex + Shift)][PairIndex];
        ctSafe: StringTemporaria := StringTemporaria + CryptTableSafe[FixOffset(OriginalIndex + Shift)][PairIndex];
      end;
		end
		else
			StringTemporaria := StringTemporaria + aInput[IndiceNaEntrada];

		Inc(IndiceNaEntrada);
		
		if IndiceNaSenha = IndiceFinalDaSenha then
			IndiceNaSenha := 1
		else
			Inc(IndiceNaSenha);

		(* ====== para uso em barras de progresso ====== *)
		if IndiceNaEntrada <= TamanhoDaEntrada then
			if Assigned(OnUpdateProgress) then
				OnUpdateProgress(Self,IndiceNaEntrada);
		(* ====== para uso em barras de progresso ====== *)
	until IndiceNaEntrada > TamanhoDaEntrada;
	Result := StringTemporaria;
end;

function TDefinedCrypt.PublicKeyEncrypt(aPublicKey: TKey; aDecriptedText: String): String;
var
	LengthOfDecriptedText, DecriptedTextCurrentIndex: Cardinal;
	PairIndex: TPairIndex;
	LastIndexOfValidPublicKey, ValidPublicKeyCurrentIndex, Shift, CryptTableIndex: Byte;
	ValidPublicKey: TKey;
begin
	if (aPublicKey = '') or (aDecriptedText = '') then
	begin
		MessageBox(0,'Para que a criptografia seja poss?vel ? necess?rio que voc? informe uma chave privada e o texto a ser criptografado.','Par?metro inv?lido',MB_ICONWARNING);
		exit;
	end;
	Result := '';

	ValidPublicKey := MakeHashFromKey(aPublicKey);

	LastIndexOfValidPublicKey := Length(ValidPublicKey);
	ValidPublicKeyCurrentIndex := 1;
	LengthOfDecriptedText := Length(aDecriptedText);
	DecriptedTextCurrentIndex := 1;

	(* ====== para uso em barras de progresso ====== *)
	if Assigned(OnUpdateProgress) then
		OnUpdateProgress(Self,DecriptedTextCurrentIndex);
	(* ====== para uso em barras de progresso ====== *)

	repeat
		if CheckIfExist(aDecriptedText[DecriptedTextCurrentIndex]) then
		begin
			Shift := GetIndexFromPair(ValidPublicKey[ValidPublicKeyCurrentIndex],PairIndex);

			CryptTableIndex := GetIndexFromPair(aDecriptedText[DecriptedTextCurrentIndex],PairIndex);

      case FCryptTable of
        ctFull: Result := Result + CryptTableFull[FixOffset(CryptTableIndex + Shift)][PairIndex];
        ctNoControl: Result := Result + CryptTableNoControl[FixOffset(CryptTableIndex + Shift)][PairIndex];
        ctSafe: Result := Result + CryptTableSafe[FixOffset(CryptTableIndex + Shift)][PairIndex];
      end;
		end
		else
			Result := Result + aDecriptedText[DecriptedTextCurrentIndex];

		Inc(DecriptedTextCurrentIndex);

		if ValidPublicKeyCurrentIndex = LastIndexOfValidPublicKey then
			ValidPublicKeyCurrentIndex := 1
		else
			Inc(ValidPublicKeyCurrentIndex);

		(* ====== para uso em barras de progresso ====== *)
		if DecriptedTextCurrentIndex <= LengthOfDecriptedText then
			if Assigned(OnUpdateProgress) then
				OnUpdateProgress(Self,DecriptedTextCurrentIndex);
		(* ====== para uso em barras de progresso ====== *)
	until DecriptedTextCurrentIndex > LengthOfDecriptedText;
end;

function TDefinedCrypt.PrivateKeyDecrypt(aPrivateKey: TKey; aEncryptedText: String): String;
var
	LengthOfEncryptedText, EncryptedTextCurrentIndex: Cardinal;
	PairIndex: TPairIndex;
	LastIndexOfPrivateKey, PrivateKeyCurrentIndex, Shift, CryptTableIndex: Byte;
	ValidPrivateKey, PublicKey: TKey;
begin
	if (aPrivateKey = '') or (aEncryptedText = '') then
	begin
		MessageBox(0,'Para que a descriptografia seja poss?vel ? necess?rio que voc? informe uma chave p?blica e o texto a ser desencriptografado.','Par?metro inv?lido',MB_ICONWARNING);
		exit;
	end;

	Result := '';

	(* Obtendo a chave p?blica *)
	PublicKey := MakeHashFromKey(aPrivateKey);

	(* Validando a chave p?blica *)
	ValidPrivateKey := MakeHashFromKey(PublicKey);

	LastIndexOfPrivateKey := Length(ValidPrivateKey);
	PrivateKeyCurrentIndex := 1;
	LengthOfEncryptedText := Length(aEncryptedText);
	EncryptedTextCurrentIndex := 1;

	(* ====== para uso em barras de progresso ====== *)
	if Assigned(OnUpdateProgress) then
		OnUpdateProgress(Self,EncryptedTextCurrentIndex);
	(* ====== para uso em barras de progresso ====== *)

	repeat
		if CheckIfExist(aEncryptedText[EncryptedTextCurrentIndex]) then
		begin
      case FCryptTable of
        ctFull: Shift := (High(TCryptTableFullInterval) + 1);
        ctNoControl: Shift := (High(TCryptTableNoControlInterval) + 1);
        ctSafe: Shift := (High(TCryptTableSafeInterval) + 1);
        else
          raise Exception.Create('Vetor criptogr?fico n?o definido');
      end;

			Shift := Shift - GetIndexFromPair(ValidPrivateKey[PrivateKeyCurrentIndex],PairIndex);

			CryptTableIndex := GetIndexFromPair(aEncryptedText[EncryptedTextCurrentIndex],PairIndex);

      case FCryptTable of
        ctFull: Result := Result + CryptTableFull[FixOffset(CryptTableIndex + Shift)][PairIndex];
        ctNoControl: Result := Result + CryptTableNoControl[FixOffset(CryptTableIndex + Shift)][PairIndex];
        ctSafe: Result := Result + CryptTableSafe[FixOffset(CryptTableIndex + Shift)][PairIndex];
      end;
		end
		else
			Result := Result + aEncryptedText[EncryptedTextCurrentIndex];

		Inc(EncryptedTextCurrentIndex);

		if PrivateKeyCurrentIndex = LastIndexOfPrivateKey then
			PrivateKeyCurrentIndex := 1
		else
			Inc(PrivateKeyCurrentIndex);

		(* ====== para uso em barras de progresso ====== *)
		if EncryptedTextCurrentIndex <= LengthOfEncryptedText then
			if Assigned(OnUpdateProgress) then
				OnUpdateProgress(Self,EncryptedTextCurrentIndex);
		(* ====== para uso em barras de progresso ====== *)
	until EncryptedTextCurrentIndex > LengthOfEncryptedText;
end;

function TDefinedCrypt.GenerateKeyPair: TKeyPair;
var
	i: Byte;
begin
	ZeroMemory(@Result,SizeOf(TKeyPair));

	Randomize;

	(* Gerando uma chave p?blica de 4096 bits (512 Bytes) *)
	for i := 0 to 255 do
		Result.PrivateKey := Result.PrivateKey + CryptTableFull[Random(High(TCryptTableFullInterval))];

  Result.PrivateKey := MakeHashFromKey(Result.PrivateKey);
  Result.PublicKey := MakeHashFromKey(Result.PrivateKey);
end;

end.
